package edu.tfh.s2.ehkbot.daten;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Vector;

/**
 * Protokoll Sammelt Informationen aus den System.
 * 
 * <pre>
 * * Informationen : Ware wurde 
 * * Warnungen : Mögliche Fehler wie mehrfach deaktivierungen 
 * * Fehler : Ware konte nicht gefunden werden 
 * * Exception : Fehler die von System mit einer Exception beantwortet wurden, 
 * 		Fehler und Exception überschneiden sich, da Fehler oft mit Exception beantwortet werden.
 * </pre>
 * 
 * @author s2zehn
 */
public class Protokoll {
	/**
	 * Speichert die Einträge vom Protokoll.
	 */
	private class ProtokollStatusEintrag {
		private Exception exp;
		private String sender;
		private StackTraceElement[] stackTrace;
		private String text;
		private TYP typ;
		private int verbose;
		private Date zeit;

		public ProtokollStatusEintrag(
										String text,
										String sender,
										int verbose,
										Date zeit,
										TYP typ,
										Exception exp,
										StackTraceElement[] stackTrace) {
			this.text = text;
			this.sender = sender;
			this.verbose = verbose;
			this.zeit = zeit;
			this.typ = typ;
			this.exp = exp;
			this.stackTrace = stackTrace;
		}
	}

	/**
	 * Typ des Logbucheintrags.
	 */
	public enum TYP {
		EXCEPTION, FEHLER, INFO, WARNUNG
	}

	/**
	 * Gibt an wie die Ausgabe das Datum Formatiert.
	 */
	private static final SimpleDateFormat format = new SimpleDateFormat("HH:mm yyyy-MM-dd");

	private static Protokoll instance;

	/**
	 * Singelton Methde.
	 * 
	 * @return Die einzige Instanz von Protokoll
	 */
	public static Protokoll getInstanc() {
		if (instance == null) {
			instance = new Protokoll();
		}
		return instance;
	}

	private Vector<ProtokollStatusEintrag> protokoll = new Vector<ProtokollStatusEintrag>();

	/**
	 * Privater Konstruktor, siehe Singelton Pattern.
	 */
	private Protokoll() {
	}

	/**
	 * Protokolliert die Exception im Protokoll.
	 * 
	 * @param e
	 *            Die Exception.
	 */
	public void exception(Exception e) {
		StackTraceElement[] stack = e.getStackTrace();
		String expname = e.getClass().getName();
		String classname = stack[1].getClassName();
		String methodname = stack[1].getMethodName();
		exception(classname, String.format("In %s wurde %s gefangen", methodname, expname), 2, e);
		if (Einstellungen.getInstance().containsKey("PrintStackTrace")
						&& Einstellungen.getInstance().getBooleanPropertie("PrintStackTrace")) {
			e.printStackTrace();
		}
	}

	/**
	 * Protokolliert die Exception im Protokoll.
	 * 
	 * @param sender
	 *            Der Sender (Klasse u. Methode) in den die Exception gefangen wurde.
	 * @param text
	 *            Eine Beschreibung des Problems.
	 * @param verbose
	 *            Das Verbose Level.
	 * @param exp
	 *            Die gefangene Exception.
	 */
	public void exception(String sender, String text, int verbose, Exception exp) {
		protokolliere(sender, text, verbose, TYP.EXCEPTION, exp);
	}

	/**
	 * Protokolliert einen Fehler im Protokoll.
	 * 
	 * @param sender
	 *            Der Sender (Klasse u. Methode) in den der Protokolleintrag gemacht wurde.
	 * @param text
	 *            Eine Beschreibung des Fehlers.
	 * @param verbose
	 *            Das Verbose Level.
	 */
	public void fehler(String sender, String text, int verbose) {
		protokolliere(sender, text, verbose, TYP.FEHLER, null);
	}

	/**
	 * Liefert das Protokoll gefiltert nach Verbose Level.
	 * 
	 * @param verbose
	 *            Ab welchen Verbose Level sollen einträge gefiltert werden.
	 * @return Ein String mit den Einträgen.
	 */
	public String getProtokoll(int verbose) {
		return getProtokoll(verbose, TYP.values());
	}

	/**
	 * Liefert das Protokoll als String zurück, gefiltert nach den Parametern.
	 * 
	 * @param verbose
	 *            Ab welchen Verbose Level sollen einträge gefiltert werden.
	 * @param typen
	 *            Welche Typen von Protokolleinträgen sollen zurück gegeben werdem.
	 * @return Ein String mit den Einträgen.
	 */
	public String getProtokoll(int verbose, TYP[] typen) {
		StringWriter sw = new StringWriter();
		PrintWriter pw = new PrintWriter(sw);
		for (int i = 0; i < protokoll.size(); ++i) {
			ProtokollStatusEintrag eintrag = protokoll.get(i);
			if (eintrag.verbose <= verbose && istIn(eintrag.typ, typen)) {
				pw.printf(
					"%s(typ=%s | verb=%d | date=%s):%s \n",
					eintrag.sender,
					eintrag.typ.toString(),
					eintrag.verbose,
					format.format(eintrag.zeit),
					eintrag.text);
			}
		}
		return sw.getBuffer().toString();
	}

	/**
	 * Protokolliert eine Information.
	 * 
	 * @param sender
	 *            Der Sender (Klasse u. Methode) in den der Protokolleintrag gemacht wurde.
	 * @param text
	 *            Eine Beschreibung der Information.
	 * @param verbose
	 *            Das Verbose Level.
	 */
	public void info(String sender, String text, int verbose) {
		protokolliere(sender, text, verbose, TYP.INFO, null);
	}

	/**
	 * Hilfsfunktiun um zu bestimmen ob such ein typ in einer Liste von typen befindet.
	 * 
	 * @param typ
	 *            Der zu suchende Typ.
	 * @param typen
	 *            Die zu druchsuchende Liste.
	 * @return true fals typ ein Elemenet welches in typen vorhanden ist, ist.
	 */
	private boolean istIn(TYP typ, TYP[] typen) {
		if (typen == null)
			return false;
		for (int i = 0; i < typen.length; ++i) {
			if (typ.equals(typen[i]))
				return true;
		}
		return false;
	}

	/**
	 * Protokolliert ein Ereigniss.
	 * 
	 * @param sender
	 *            Der Sender (Klasse u. Methode) in den der Protokolleintrag gemacht wurde.
	 * @param text
	 *            Eine Beschreibung des Problems.
	 * @param verbose
	 *            Das Verbose Level.
	 * @param typ
	 *            Der Typ des Prokolleintrags.
	 */
	public void protokolliere(String sender, String text, int verbose, TYP typ) {
		protokolliere(sender, text, verbose, typ, null);
	}

	/**
	 * Protokolliert ein Ereigniss.
	 * 
	 * @param sender
	 *            Der Sender (Klasse u. Methode) in den der Protokolleintrag gemacht wurde.
	 * @param text
	 *            Eine Beschreibung des Problems.
	 * @param verbose
	 *            Das Verbose Level.
	 * @param typ
	 *            Der Typ des Prokolleintrags.
	 * @param exp
	 *            Eine gefangene Exception, fals eine vorliegt ansonsten null.
	 */
	public void protokolliere(String sender, String text, int verbose, TYP typ, Exception exp) {
		protokoll.add(new ProtokollStatusEintrag(
			text,
			sender,
			verbose,
			Zeitgeber.getInstance().getCurrentDate(),
			typ,
			exp,
			Thread.currentThread().getStackTrace()));
	}

	/**
	 * Protokolliert ein Warnung.
	 * 
	 * @param sender
	 *            Der Sender (Klasse u. Methode) in den der Protokolleintrag gemacht wurde.
	 * @param text
	 *            Eine Beschreibung der Warnung.
	 * @param verbose
	 *            Das Verbose Level.
	 */
	public void warnung(String sender, String text, int verbose) {
		protokolliere(sender, text, verbose, TYP.WARNUNG, null);
	}
}